home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Medal Software 3
/
Gold Medal Software - Volume 3 (Gold Medal) (1994).iso
/
music
/
melody21.arj
/
MELODY21.EXE
/
SOUNDSYS.C
< prev
next >
Wrap
Text File
|
1992-07-26
|
14KB
|
254 lines
/* ------------------------------------------------------------------- */
/* SOUNDSYS.C - Background sound system for the IBM-PC and compatibles */
/* ------------------------------------------------------------------- */
/* Written by Juan Jimenez */
/* Micro Consulting Associates */
/* 868 Ashford 6B */
/* San Juan, P.R. 00907-1018 */
/* (809) 725-9470 FAX (809) 721-8470 GEmail: J.JIMENEZ */
/* For Turbo C 1.5 or higher, though can be ported... */
/* Released on 3/7/89 */
/* ------------------------------------------------------------------- */
/* This code is hereby placed in the PUBLIC DOMAIN. This means that I */
/* am giving it to you at no charge, and you can do with it whatever */
/* you want, without any restrictions or requests for donations, */
/* (though if you feel like it I'd be happy if you sent me $10, but if */
/* you don't feel like it that's fine with me too). I'm, putting it */
/* the public domain because I've had a good response to some of my */
/* shareware stuff (like GSETUP), and I feel I should give something */
/* nice in return to the community, for free. The only thing that I */
/* -DO- ask if that if you use it, please give me credit for this */
/* implementation, and NEUROMANCER for the original one... */
/* ------------------------------------------------------------------- */
/* This file contains the C source code for a background sound system */
/* that can be used in games and other types of applications that want */
/* to produce sounds and noises in the background without interfering */
/* with the current foreground tasks (like the game play code...) */
/* ------------------------------------------------------------------- */
/* The reason I coded this was because I couldn't find anything that */
/* would do the job. This code is loosely based on NEUROMANCER's code */
/* which he graciously sent to me as a base to start off with. His */
/* played a predefine sound set, I allow free-form input to produce */
/* just about any kinds of sounds for all kinds of purposes as long as */
/* the sounds are not overly complex. The sampling rate here is 500hz */
/* which means that the lowest period of time that a particular note */
/* can be played is 1/500th of a second. This should be more than */
/* sufficient for most complex applications and games. */
/* ------------------------------------------------------------------- */
/* The way this code works is as follows: */
/* */
/* 1) Before you do anything with this sound system, you have to make */
/* sure that you initialize it. This is done with one simple call to */
/* init_sound(), with no arguments. init_sound redirects interrupt 8, */
/* the hardware timer interrupt (clock tick) from it's destination */
/* inside DOS to ourselves. We save that vector 'cause we'll need it */
/* later on, as you shall soon see. It then modifies the system timer */
/* to issue 500 ticks per second instead of 18.2. This clock signal */
/* is the heart of the sound system. We use 500 clock ticks because */
/* it is A) a nice round number and B) about as slow a tick as you can */
/* have to produce smooth sound. */
/* */
/* 2) Once you call init_sound(), the system is running and ready to */
/* receive input. This is done with the submit_sound() routine. This */
/* routine takes two arguments, both integer values. The first is the */
/* frequency of the sound to be generated, the second is the duration */
/* of the sound in 1/500th's of a second. submit_sound keeps a queue */
/* which can hold up to 8k submissions. It has two possible return */
/* values. If the queue had room left and the submission was posted, */
/* it returns an integer value of 0. If the queue was full, the entry */
/* you sent to it is thrown away and you get back a value of -1. That */
/* is about as simple as it can get. */
/* */
/* 3) Every time a clock tick is generated (500 times a second), the */
/* soundsystem() interrupt service routine takes over. It first checks */
/* to see how many clock ticks have been issued. If 27 ticks have been */
/* issued (count is kept in tickcount), we make a quick call to the */
/* DOS timer interrupt service routine. This effectively emulates the */
/* standard DOS clock. If we did not do this, the clock would never be */
/* updated while the sound system is running, or your clock would go */
/* to Warp 10 and by the time you finished your clock would be set to */
/* some date in the next century... If 27 ticks have gone by, we reset */
/* that counter as well. Now, the variable backduration is the one */
/* that keeps track of a sound which is currently being played. If */
/* backduration is greater than 0, that means a sound is in progress. */
/* We simply decrement backduration by one, and return to your program.*/
/* If backduration is 0, we check to see if there are no sounds in the */
/* queue. If there are, we start the sound going with the specified */
/* frequency, set backduration to the number of ticks it should last, */
/* and return. If there are no sounds in the queue, we set the sound */
/* to a random tone limited by the value in the global variable */
/* "frequency". The background random noise is controlled by the var */
/* "back_sound". If you set it to "ON" the background noise is on, if */
/* you set it to "OFF" the noise is turned off. */
/* */
/* 4) Once your program is finished, you MUST remember to call the */
/* restore_sound() routine. This gives back the timer to DOS and */
/* resets the system timer back to the normal 18.2 ticks per second. */
/* ------------------------------------------------------------------- */
/* LIMITATIONS AND CAVEATS! READ THIS CAREFULLY! */
/* ------------------------------------------------------------------- */
/* 1) This routine CANNOT be active while debugging your code in the */
/* REMOTE DEBUG mode of Turbo Debugger. It WILL crash the debugger. */
/* By remote debugging I mean the use of TDREMOTE to debug code that */
/* is running on one machine with another machine via a serial port. */
/* */
/* 2) This routine will most likely interfere with network adapters, */
/* though I don't know why. I just tell people not to run my programs */
/* that use this stuff on a network workstation. */
/* */
/* 3) This code was written on 80286 and 80386 boxes. On 4.77 mhz 8088 */
/* machines it does not do as well I would like it to. You can try */
/* making the soundsystem() routine smaller, but the only way I can */
/* think of that is going to assembly language, and even then this */
/* is pretty small as it is, so... You can also try and reduce the */
/* sampling rate, which is set in init_sound(). You do have to know */
/* how the system timer chip runs to program it. If you need help, do */
/* call or send me e-mail on GEnie. */
/* ------------------------------------------------------------------- */
/* That's it. How's that for documentation, eh? hehe... */
/* ------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <time.h>
#include "soundsys.h"
noise background[noisemax]; /* The sound queue itself */
int frequency=0; /* Random frequency base value */
int back_sound; /* Flag for background sound on or off */
int topindex=0; /* Marks the top of the queue */
int backduration=0; /* Keeps track of duration of sound */
int backindex=0; /* Marks bottom of queue */
int tickcount=0; /* Keeps track of clock ticks for INT 8 */
int sound_in_queue = 0; /* Tells if sound in queue */
int insound; /* Prevent reentrancy */
void interrupt (*oldint8)(void); /* Holds old INT 8 vector */
/* ---------------------------------------------------------- */
/* Must be called prior to use of sound variables, sets up a */
/* int 8h ISR with the interrupt function soundsystem. */
/* If init_sound is called, then program must call */
/* restore_sound before it exits (or bad things will happen)! */
/* ---------------------------------------------------------- */
void init_sound(void)
{
backduration = 5; /* Init backduration */
randomize(); /* Seed random number gen. */
backindex = topindex = 0; /* Reset queue pointers */
insound = FALSE; /* Init insound */
back_sound = OFF; /* No background sound */
oldint8 = getvect(TimerTick); /* get the original vector */
disable(); /* Ints off to set timer */
outportb(0x043,0x034); /* Counter 0, read lsb-msb */
/* mode 2, rate generator, */
/* divide by N (0x04a9h) */
outportb(0x040,0x052); /* Low byte of 0x004A9 */
outportb(0x040,0x009); /* High byte of 0x004A9 */
/* Clock tick is now 1000 */
/* ticks per second... */
setvect(TimerTick,soundsystem); /* set up our ISR. */
enable(); /* ...and off we go... */
}
/* -------------------------------------------- */
/* Call this routine before returning to DOS... */
/* -------------------------------------------- */
void restore_sound(void)
{
nosound(); /* Turn off sound */
disable(); /* Interrupts off */
outportb(0x043,0x034); /* Counter 0, read lsb-msb */
/* mode 2, rate generator, */
/* divide by N */
outportb(0x040,0x000); /* Low byte of 0x00000 */
outportb(0x040,0x000); /* High byte of 0x00000 */
/* Clock tick is now 18.2 */
/* ticks/sec (normal rate) */
setvect(TimerTick,oldint8); /* Restore timer ISR */
enable(); /* ...and off we go... */
}
#pragma warn -par /* Turn off warnings that I know about here */
#pragma warn -aus
#pragma warn -use
/* --------------------------------------- */
/* The heart of the background soundsystem */
/* --------------------------------------- */
void interrupt soundsystem(RegIntList)
{
unsigned temp[3]; /* temporary storage area */
register int dummy1; /* Stop use of register vars */
register int dummy2; /* ...ditto... */
outportb(0x020,0x020); /* Send EOI to 8259 PIC */
if (!insound) /* Prevent reentrancy */
{
insound = TRUE; /* Ok, we're in, lock the door... */
if (backduration) backduration--; /* Sound is in progress... */
else
{
if (sound_in_queue) /* Current sound finished, more? */
{
backduration = (background[backindex].duration); /* Yes, setup */
sound(background[backindex++].freq); /* Start sound */
if (backindex == topindex) /* If last sound reset */
backindex=topindex=sound_in_queue=0; /* queue pointers */
}
else if (back_sound) sound(random(frequency)); /* No sounds, do background noise */
else nosound();
}
insound = FALSE; /* Open the door! */
}
tickcount++; /* Increment tick count */
if (tickcount>27) /* Have we had 27 ticks? */
{
tickcount=0; /* Yes, reset count to 0 */
/* chain to clock ISR */
temp[0] = bp; /* Save top 3 values */
temp[1] = di;
temp[2] = si;
bp = ds; /* Move all others up by 3 */
di = es;
si = dx;
ds = cx;
es = bx;
dx = ax;
cx = FP_OFF(oldint8); /* and put a new ip/cs/flags */
bx = FP_SEG(oldint8); /* on the stack */
ax = flags & ~0x200;
_BP -= 6; /* Modify BP by three words */
return; /* NOTE: after changing _BP, don't */
/* plan on using an local variables, */
/* other than those here. */
}
}
#pragma warn +use /* Turn the warnings back on! */
#pragma warn +aus
#pragma warn +par
/* --------------------------------------------------------- */
/* This routine is used to submit sounds to the sound queue. */
/* ----------------------------------------------------------*/
int submit_sound(int freq,int delay)
{
if (backindex < noisemax) /* Queue full? */
{
background[topindex].freq = freq; /* No, put it in queue */
background[topindex++].duration = delay;
sound_in_queue=1; /* Note sound in queue */
return(0); /* Return OK value */
}
else return(-1); /* Queue is full, return fail result */
}
/* Finis... */